#include<iostream>
#include<ostream>
#include<string>
#include<type_traits>

using namespace std;

template<class T,class...>
struct get_descriptor {
	const string value;
	get_descriptor() : value("unknown") {}
};

template<>
struct get_descriptor <int> {
	const string value;
	get_descriptor() :value("int") {}
};

template<>
struct get_descriptor <short> {
	const string value;
	get_descriptor() :value("short") {}
};

template<>
struct get_descriptor <char> {
	const string value;
	get_descriptor() :value("char") {}
};

template<>
struct get_descriptor <long> {
	const string value;
	get_descriptor() :value("long") {}
};

template<>
struct get_descriptor <float> {
	const string value;
	get_descriptor() :value("float") {}
};

template<>
struct get_descriptor <double> {
	const string value;
	get_descriptor() :value("double") {}
};

template<class T,class ... args>
struct get_descriptor <T , args...> {
	const string value;
	get_descriptor() :value(get_descriptor<T>().value +","+ get_descriptor<args...>().value) {}
};

template<class T>
struct get_descriptor<T*> {
	const string value;
	get_descriptor() :value(get_descriptor<T>().value + "*") {}
};

template<class T>
struct get_descriptor<T&> {
	const string value;
	get_descriptor() :value(get_descriptor<T>().value + "&") {}
};

template<class T, int N>
struct get_descriptor <T[N]> {
	char buf[14];
	const string value;
	get_descriptor() :value(get_descriptor<T>().value+"["+ itoa(N,buf,10) +"]") {}
};

template<class r,class... args>
struct get_descriptor<r(args...)> {
	const string value;
	get_descriptor():value(get_descriptor<r>().value + "(" + get_descriptor<args...>().value + ")") {}
};

template<class T>
ostream& operator << (ostream& out, get_descriptor<T> type) {
	out << type.value;
	return out;
}

int main(void) {
	cout << get_descriptor<int[3]>() << endl;
	getchar();
	return 0;
}